package com.suray.socket.system.netty.client;

import com.suray.basic.wcs.utils.LoggerType;
import com.suray.basic.wcs.utils.LoggerUtil;
import com.suray.basic.wcs.utils.ModuleType;
import com.suray.basic.wcs.utils.WcsFuncUtil;
import com.suray.socket.system.base.EchoInterface;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;

public class EchoClient implements EchoInterface {

    /**
     * 接收数据最大缓存数量
     * 理论上接收数量缓存最多为1
     */
    private static final int MAX_REC_MESSAGE_BUFFER_NUM = 20;

    /**
     * ip地址
     */
    protected final String ip;

    /**
     * 端口
     */
    protected final int port;

    /**
     * 发送数据接口句柄
     */
    private ChannelHandlerContext ctx;

    EchoClientHandler clientHandler;

    Queue<byte[]> messages = new LinkedList<>();

    protected void setCtx(ChannelHandlerContext ctx) {
        this.ctx = ctx;
    }

    /**
     * 是否在线
     *
     * @return
     */
    public boolean isOnline() {
        return ctx != null;
    }

    public EchoClient(String host, int port) {
        this.ip = host;
        this.port = port;
    }

    protected void addMessage(byte[] data) {
        LoggerUtil.debug(ModuleType.SOCKET, LoggerType.INFO, messages.size(), null,
                ip + ":" + port + "-接收到数据：" + Arrays.toString(data), this.getClass());
        synchronized (messages) {
            if (messages.size() >= MAX_REC_MESSAGE_BUFFER_NUM) {
                messages.poll();
            }

            messages.add(data);
        }
    }

    protected byte[] getMessage() {
        byte[] message;
        synchronized (messages) {
            message = messages.poll();
        }

        return message;
    }

    protected void clearMessage() {
        LoggerUtil.info(ModuleType.SOCKET, LoggerType.INFO, 0, null, "清空缓存数据：" + ip + ", " + port, this.getClass());
        synchronized (messages) {
            messages.clear();
        }
    }

    @Override
    public byte[] recData() {
        return getMessage();
    }

    @Override
    public void sendData(byte[] data) throws IOException {
        if (ctx != null) {
            LoggerUtil.debug(ModuleType.SOCKET, LoggerType.INFO, 0, null, ip + ":" + port + "-发送数据："
                    + WcsFuncUtil.changeToHexString(data), this.getClass());
//            ByteBuf bf = Unpooled.copiedBuffer(Unpooled.copiedBuffer(str, CharsetUtil.UTF_8));
            ByteBuf bf = Unpooled.copiedBuffer(data);
//            String s = bf.toString(CharsetUtil.UTF_8);
            ctx.writeAndFlush(bf);
        } else {
            throw new IOException();
        }
    }

    @Override
    public void start() throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();

        try {
            Bootstrap bootstrap = new Bootstrap();//1.创建Bootstrap
            clientHandler = new EchoClientHandler(this);
            bootstrap.group(group)//2.指定EventLoopGroup来处理客户端事件，由于我们NIO传输，所以用到了NioEventLoopGroup的实现
                    .channel(NioSocketChannel.class)//3.使用的Channel类型是一个用于NIO传输，也可以使用和服务器不一样的类型
                    .remoteAddress(new InetSocketAddress(ip, port))//4.设置服务器的InetSocketAddress
                    .handler(new ChannelInitializer<SocketChannel>() {//5.当建立一个连接和一个新的通道时，创建添加到 EchoClientHandler 实例 到 channelpipeline
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(clientHandler);
                        }
                    });
            ChannelFuture f = bootstrap.connect().sync();//6.连接到远程，等待连接完成
            f.channel().closeFuture().sync();//7.阻塞直到Channel关闭
        } finally {
            ctx = null;
            clearMessage();
            group.shutdownGracefully().sync();//8.调用shutdownGracefully来关闭线程池和释放所有资源
        }
    }

    public void close() {
        try {
            if (ctx != null) {
                ctx.close();
//                ctx.deregister();
//                ctx.pipeline().remove(clientHandler);
//                clientHandler.channelInactive(ctx);
                ctx = null;
            }
        } catch (Exception e) {
            LoggerUtil.error(ModuleType.SOCKET, LoggerType.INFO, 0, "连接关闭失败", ip + ":" + port,
                    this.getClass());
        }
    }

    public static void main(String[] args) throws Exception {
        final String host = "127.0.0.1";
        final int port = 8990;

        ArrayList<EchoClient> clients = new ArrayList<>();
        new Thread(() -> {
            while (true) {
                try {
                    if (clients.size() > 1) {
                        EchoClient client = clients.get(0);
                        if (client.isOnline()) {
                            client.close();
                            clients.remove(0);
                            System.out.println("关闭成功");
                        }
                    }
                    Thread.sleep(100);
                } catch (Throwable e) {
                    e.printStackTrace();
                }
            }
        }).start();
        while (true) {
            new Thread(() -> {
                try {
                    System.out.println("开启连接");
                    EchoClient client = new EchoClient(host, port);
                    clients.add(client);
                    client.start();
                } catch (Throwable e) {
                    e.printStackTrace();
                }
            }).start();

            Thread.sleep(1000);
        }
    }

    /**
     * 清空缓存数据
     */
    public void clearBuffer() {
        messages.clear();
    }
}
